#pragma once
#include<iostream>
#include<cstring>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<wait.h>
#include"Log.hpp"
#include"InetAddr.hpp"
#include"ThreadPool.hpp"
using namespace log_ns;
enum 
{
    SOCKET_ERROR=1,
    BIND_ERROR,
    LISTEN_ERROR
};
const static int gport=8888;
const static int gsock=-1;
const static int gblcklog=8;
using task_t=std::function<void()>;
class TcpServer
{
    public: 
    TcpServer(uint16_t port = gport)
        : _port(gport),
          _listensockfd(gsock),
          _isrunning(false)

    {

    }
    void InitServer()
    {
        //1.创建套接字
        _listensockfd=::socket(AF_INET,SOCK_STREAM,0);//第一个参数是证明他是tcp，证明他是面向链接，链接就是套接字不可以直接进行对话而是需要创建链接再来进行对话
        if(_listensockfd<0)
        {
            exit(SOCKET_ERROR);
            LOG(FATAL,"socket creat error\n");

        }
        LOG(INFO,"socket create success,sockfd %d\n",_listensockfd);

        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(_port);
        local.sin_addr.s_addr=INADDR_ANY;//绑定ip地址为0；
        //2.绑定
        // std::cout<<::bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<<std::endl;
        // std::cout<<(::bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)<<std::endl;
        // // if(::bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)
        if(::bind(_listensockfd,(struct sockaddr*)&local,sizeof(local))<0)
        {
            LOG(FATAL,"bind error\n");
            exit(BIND_ERROR);
        }
         
 
        LOG(INFO,"bind success\n");
   
        //因为tcp是面向链接的客户端和服务器不能之间发消息；需要先建立链接 再发消息
        //3.tcp需要未来不断的能够获取链接
        //解决：将套接字设置为liten状态，tcp就可以一直获取链接；
        if(::listen(_listensockfd,gblcklog)<0)
        {
            LOG(FATAL,"listen error\n");
            exit(LISTEN_ERROR);
        }
        else
        LOG(INFO,"listen success\n");
        //这里将套接字改为listen状态这时其他端也就可以直接建立链接了
        //就好比餐厅中的老板一直在餐厅中等待顾客

    }
    class ThreadDate
    {
        public:
            int _sockfd;
            TcpServer *_self;
            InetAddr _addr;
        public:
            ThreadDate(int sockfd,TcpServer *self ,const InetAddr &addr):_sockfd(sockfd),_self(self),_addr(addr)         
            {

            }
    };
    void Loop()
    {
        _isrunning=true;
        while(_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            //4获取新链接          招客店员
            int sockfd=::accept(_listensockfd,(struct sockaddr *)&client,&len);
            if(sockfd<0)  //提供服务的人员
            {
                LOG(FATAL,"accept error\n");
                continue;
                //这里不应该退出就像张三在揽客失败后会重新去揽客
            }
            InetAddr addr(client);
            LOG(INFO,"get a new link ,client info: %s \n",addr.AddrStr().c_str());
            //5.对收到的数据进行处理
        // version 0--不靠谱版本
            // Service(sockfd,addr);
        // version 1--多进程版本//多进程版本会将子进程会将父进程的地址空间间和文件描述符表传递给子进程；
        //     pid_t id=fork();//写实拷贝
        //     if(id==0)       //这里子进程进行数据处理，父进程链接 accept
        //     {
        //         ::close(_listensockfd);
        //         if(fork()>0)exit(1);//这一行是子进程
        //         Service(sockfd,addr);//下面这两行是孙子进程 这里的孙子进程被孤儿进程了和父进程没有关系了
        //         exit(0);

        //     }
        //     //上面是一种方法也也可以使用signal(SIGCHLD, SIG_IGN) 这个是将子进程给父进程的信号终止不要发
        //     ::close(sockfd);     //要使用这个语句直接写在父进程代码内就好了
        //     int n=waitpid(id,nullptr,0);
        //     if(n>0)
        //     {
        //         LOG(INFO,"wait child success.\n");
        //     }

        //     sleep(1);
        // }
        // version2 ----多线程版本
        // pthread_t tid;
        // ThreadDate *td=new ThreadDate(sockfd,this,addr);
        // pthread_create(&tid,nullptr,Execute,td);
        // // pthread_join(tid);这里主进程就不要等待了
        //version3 线程池版本int sockfd, InetAddr addr
        // task_t t=std::bind(&TcpServer::Service,this,sockfd,addr);
        // ThreadPool<task_t>::GetInstance()->Equeue(t);
         // version2 ----多线程版本
        pthread_t tid;
        ThreadDate *td=new ThreadDate(sockfd,this,addr);
        pthread_create(&tid,nullptr,Execute,td);

        }
        _isrunning=false;
       
    }
    static void * Execute(void * args)
    {
        pthread_detach(pthread_self());//解除当前线程和父线程之间的联系
        ThreadDate *td=static_cast<ThreadDate *>(args);
        td->_self->Service(td->_sockfd,td->_addr);
        ::close(td->_sockfd);
        delete td;
        return nullptr;

    }
    //5.
    void Service(int sockfd, InetAddr addr)
    {
        //长服务
        while(true)
        {
            //这里是流字节流  tcp 可以直接使用流的接口来使用
            char inbuffer[1024];
            ssize_t n=::read(sockfd,inbuffer,sizeof(inbuffer)-1);//店内的人元就像是一个管文件，从中读取
            //这里建立了链接就像管道中读取                       
            if(n>0)//成功夺取
            {
                std::string echo_string ="[server echo] #";
                echo_string+=inbuffer;
                write(sockfd,echo_string.c_str(),echo_string.size());

            }
            else if(n==0)//发送方下线了
            {
                LOG(INFO,"client %s quit\n",addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(ERROR,"read error %s\n",addr.AddrStr().c_str());
                break;
            }




            
        }

    }
    ~TcpServer()
    {

    }
private:
    uint16_t _port;
    int _listensockfd;//TODO
    bool _isrunning ;



};                                
